using CfgTable;
using Cysharp.Threading.Tasks;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using System.Collections.Generic;

namespace IQIGame.Onigao.GamePlay
{
    public class InteractStateInteractWithNPC : InteractStateBase
    {
        private enum ActionDelayType
        {
            None,
            /// <summary>
            /// 按照交互类型配置等待一段时间后返回上一个状态
            /// </summary>
            ByConfig,
            /// <summary>
            /// 等待玩家选择后返回上一个状态
            /// </summary>
            ByPlayerChoose
        }

        public InteractStateInteractWithNPC(LevelPlayRegionController regionCtrl) : base(regionCtrl)
        {
        }

        public override LevelInteractState stateId => LevelInteractState.InteractWithNPC;
        private LevelCharacterEntity character => regionCtrl?.entityCtrl?.GetMainCharacter();

        private LevelInteractState _prevStateId;
        private LevelPlayBaseEntity _interactingEntity;
        private uint _timerId;
        private CfgEntityInteractType _interactCfg;

        public override void OnInit()
        {

        }

        public override bool IsNextStateValid(LevelInteractState nextId)
        {
            return true;
        }


        private async UniTask PlayAnimation(ActionDelayType delayType)
        {
            float duration;
            if (_interactCfg == null)
            {
                character.GetAnimationComponent().PlayAttack();
                duration = 0.8f;
            }
            else
            {
                character.GetAnimationComponent().StartAnimAction(_interactCfg.ActionID);
                duration = (float)_interactCfg.Delay / 1000;
            }

            if (delayType == ActionDelayType.ByConfig)
            {
                //等待
                _timerId = TimerManager.Instance.StartOneTimer(duration);
                await _timerId.AwaitTimer();

                //需按照配置时间等待的情况才播放受击特效，如果是等待玩家选择等情况，不播放受击动作，否则必然会出现不匹配的穿帮问题
                if (_interactCfg != null && _interactCfg.IsPlayHit)
                {
                    //播放受击动作
                    _interactingEntity.GetAnimationComponent().PlayHit();
                    //播放特效
                    regionCtrl.effectCtrl.ShowEntityEffect(_interactingEntity,
                        EntityBindPointCommonType.Middle,
                        LevelDataConsts.HitEffect,
                        LevelDataConsts.HitEffectDuration).Forget();
                    //播放受击音效
                    var dynEntity = _interactingEntity.GetDynLevelEntity();
                    var soundCid = dynEntity.cfgLevelEntity.HitSound;
                    if (soundCid != 0)
                    {
                        ManagerCenter.Audio.PlayAudioById(soundCid);
                    }
                    //等待演播受击
                    _timerId = TimerManager.Instance.StartOneTimer(LevelDataConsts.InteractHitDelay);
                    await _timerId.AwaitTimer();
                }
            }
        }

        public override void OnEnter(LevelInteractState prevStateId)
        {
            this._prevStateId = prevStateId;
            this._interactingEntity = regionCtrl.entityCtrl.toInteractEntity;
            int curInteractType = character.InteractTypeWithEntity;
            if (curInteractType != 0)
            {
                this._interactCfg = TableCenter.entityInteractType.Get(curInteractType);
            }

            character.StopMoveAnimImmediately();

            if (_interactingEntity == null)
            {
                ToPreviousStateAfterPlayAnimation().Forget();
                return;
            }
            Interact().Forget();
        }

        private async UniTaskVoid Interact()
        {
            var quests = _interactingEntity.offeredQuests;
            var dialogs = GetDialogOfferedByEntity();
            var delayType = quests == null && dialogs == null ? ActionDelayType.ByConfig : ActionDelayType.ByPlayerChoose;
            //播放动画，等待动画播放的时间根据情况而定，如果包含任务选项面板则延迟为0，PlayAnimation后续的逻辑会实时执行。
            //否则需要等待动画播放一段时间（根据配置而定）继续后续逻辑
            await PlayAnimation(delayType);

            var interactEntityBeforeLeaveState = _interactingEntity;
            var interactCfg = _interactCfg;
            //LeaveState 逻辑处理完毕，返回上一个状态，离开状态后，_interactingEntity和_interactCfg会被置空
            ChangeState(this._prevStateId);
            //离开主位面时不能交互
            if (!character.dynEntity.isInMainPhasing)
            {
                return;
            }
            //触发交互，该方法虽然存在异步，但按需求不能耽误状态机切换（并行）。
            //策划的需求状态就有重叠，这里原始设计不完美，状态分离有问题，这段逻辑即便拿到另一个状态里也会存在重叠，这里不用状态机可能更好。
            InnerInteract(quests, dialogs, interactEntityBeforeLeaveState, interactCfg).Forget();
        }

        /// <summary>
        /// 交互逻辑，一旦触发，玩家可自由移动，如果打开了选项面板，角色移动时会自动关闭选项面板
        /// </summary>
        /// <param name="quests"></param>
        /// <returns></returns>
        private async UniTask InnerInteract(IReadOnlyList<int> quests, IReadOnlyList<DialogReference> dialogs, LevelPlayBaseEntity interactEntity, CfgEntityInteractType interactCfg)
        {
            //保证交互不重复打开交互选项UI
            //如果交互选项UI不经过玩家选择而关闭时会取消Task，执行打开UI时await的后续逻辑，所以最好放在最前面
            //由于交互选项UI不能阻挡摇杆，而交互按钮和摇杆都在输入UI上，所以不能通过阻挡交互按钮来规避重复交互，必须加这行代码
            //交互逻辑涉及的UI只有交互选项UI，所以这里不去拆分输入UI，避免代码混乱
            ManagerCenter.UI.CloseWindow<UIInteractOption>();

            //处理交互逻辑
            //交互类型不为0才能和npc实际交互
            //由于关卡的交互是一个独立的状态(stateMachine)和代码的特殊性，交互类型为0时传入的interactCfg会是null
            if (interactCfg != null)
            {
                //打开选项面板（包含接任务和直接交互的选项）
                if (quests != null || dialogs != null)
                {
                    //判断交互，打开面板
                    //showDefaultOption == true关卡的默认按钮一定会有，策划会配置无服务触发，实现可交互npc一定可以有交互结果的功能
                    var result = await GameScenePlayUtil.InteractWithEntity(quests, dialogs,
                        interactCfg, interactEntity, true);
                    switch (result.type)
                    {
                        case EntityInteractOptionResultType.Quest: //接任务
                            int selectedQuestCid = result.value;
                            var cfgQuest = TableCenter.quest.Get(selectedQuestCid);
                            if (cfgQuest.ReceiveStory != 0)
                            {
                                await StoryManager.Instance.StartPlayStoryById(cfgQuest.ReceiveStory, 0, interactEntity, true);
                            }
                            var netResult = await CLevelLogicModule.NetCore.CS_AcceptQuest(selectedQuestCid);
                            if (netResult.IsFail)
                            {
                                LogGame.LogError($"接任务失败，服务器返回: {netResult.code}");
                            }
                            break;
                        case EntityInteractOptionResultType.StoryDialog: //默认对话
                            int storyCid = result.value;
                            await StoryManager.Instance.StartPlayStoryById(result.value, 0, interactEntity, true);
                            PlayerModule.NetCore.CS_uploadStoryCompleted(NetProtocol.Enum.GamePlayAreaType.Level, storyCid);
                            break;
                        case EntityInteractOptionResultType.Default: //默认交互
                            RequestDefaultInteract(interactEntity).Forget();
                            break;
                        case EntityInteractOptionResultType.Cancel: //取消选择
                            LogGame.Log("取消选项");
                            break;
                        default: //非正常结束
                            break;
                    }
                }
                //直接交互
                else
                {
                    RequestDefaultInteract(interactEntity).Forget();
                }
            }
        }

        private IReadOnlyList<DialogReference> GetDialogOfferedByEntity()
        {
            var entityConfig = _interactingEntity.GetDynLevelEntity().levelEntityConfig;
            if (entityConfig.paramMap.TryGetValue(EntityDynamicParamType.StoryDialog, out var dynParam))
            {
                return (dynParam as EntityParamStoryDialog).dialogs;
            }
            return null;
        }

        private async UniTaskVoid RequestDefaultInteract(LevelPlayBaseEntity interactingEntity)
        {
            var result = await CLevelLogicModule.NetCore.CS_Interact(interactingEntity.id);
            //注意如果策划配置了和npc交互退出关卡，此时关卡已经销毁了
            if (result.IsFail)
            {
                LogGame.Log("交互失败:" + result.code);
            }
            else if (!result.anyExecDone)
            {
                LogGame.Log("交互成功，但成功运行的执行数量是0。");
            }
#if UNITY_EDITOR
            else
            {
                LogGame.Log($"交互成功，类型:{character?.InteractTypeWithEntity.ToString()}。");
            }
#endif
        }

        public override void OnEvent(LevelInteractEvent eventId, params object[] args)
        {
            if (!this.active)
            {
                return;
            }
            switch (eventId)
            {
                case LevelInteractEvent.ClickInteract:
                    //攻击重置
                    if (_timerId > 0)
                    {
                        _timerId.StopTimer();
                        _timerId = 0;
                        ToPreviousStateAfterPlayAnimation().Forget();
                    }
                    break;
            }
        }

        private async UniTaskVoid ToPreviousStateAfterPlayAnimation()
        {
            await PlayAnimation(ActionDelayType.ByConfig);
            ChangeState(this._prevStateId);
        }

        public override void OnUpdate(float deltaTime)
        {
            if (_interactCfg != null && _interactCfg.IsHeroRotate && _interactingEntity != null && character != null)
            {
                character.transform.LookAt(_interactingEntity.transform);
            }
        }

        public override void OnExit()
        {
            _prevStateId = LevelInteractState.None;
            _interactingEntity = null;
            _interactCfg = null;
        }

        public override void Dispose()
        {

        }


    }
}
